#ifndef __tenacitas_buffer__byte_array__h__
#define __tenacitas_buffer__byte_array__h__

// =============================================================================
/** @file */


// =============================================================================
//                                 C++ Headers
#include <cstdint>
#include <iostream>

// =============================================================================
//                                 3rds Headers


// =============================================================================
//                                 Our Headers


// =============================================================================
//                                 Namespaces
using namespace std;


// =============================================================================
//                                 Macro Contants
/** */


// =============================================================================
//                                 Macro Commands
/** */


/** namespace of the organization */
namespace tenacitas {

    /** namespace for the project */
    namespace buffer {

        // =========================== Typedefs ===========================
        /** */
        typedef unsigned char byte_type;
        typedef uint64_t size_type;

        // ============================ Pre-Declarations ==================
        /** */

        class fixed_byte_array;
        class variable_byte_array;
        class byte_array;
        
        
        // ============================ Attributes ========================
        /** */

        // ============================ Functions =========================
        /** */

        // ============================ Classes ===========================
        /** 

            @nosubgrouping 
        */
        class byte_array {

            // --- byte_array - Friends ----------------------------------

            friend ostream &
            operator << ( ostream & p_out,
                          const byte_array & p_array ) { 
                for( size_type l_i = 0; l_i < p_array.m_current; ++l_i ) {
                    p_out << std::ios::width( 2 ) << std::ios::fill( '0' ) <<
                        static_cast<uint16_t>( p_array.m_data[l_i] ) << " ";
                }
                return p_out;
            }

        public:

            // --- byte_array - Public Constructors -------------------------
            virtual ~byte_array( );
            
            
        protected:

            // --- byte_array - Private Accessors -------------------------

            /** Equal-to */
            bool operator ==( const byte_array & p_right ) const;

            /** Not-equal-to */
            bool operator !=( const byte_array & p_right );

            /** Assignment operator */
            byte_array & operator = ( const byte_array & p_right );

            /** Retrieves the byte at a certain position

                @param [in] p_pos is a position in the byte array

                @throw if it was not possible to access @p p_index 
             */
            byte_type & operator[]( size_type p_pos );

            /** To iterate the byte array, from the begining to the end

                @tparam t_functor must implement:
                <tt>void operator( )( byte_type p_byte, size_type p_pos )</tt>,
                where @p p_byte is the value in @p p_pos
             */
            void forward( t_functor & p_functor );

            /** To iterate the byte array, from the end to the begining

                @tparam t_functor must implement:
                <tt>void operator( )( byte_type p_byte, size_type p_pos )</tt>,
                where @p p_byte is the value in @p p_pos
             */
            void backward( t_functor & p_functor );           

            /** Tries to find the position of a byte

                @param [in] p_byte is the byte being searched
                @param [out] p_pos is the position of the @p p_byte, if found

                @return @p true id @p p_byte was found, @p false otherwiswe
             */
            bool find( const t_byte_type p_left, size_type & p_pos );

            /** Tries to retrieve a reference to a byte

                @param [in] p_byte is the byte being searched
                @param [out] p_found is a reference to the @p p_byte, if found

                @return @p true if @p p_byte was found, @p false otherwiswe
             */
            bool find( const t_byte_type p_left, byte_type * p_found );

            // --- byte_array - Protected Attributes ---------------------------

            /** Size of the byte array */
            size_type m_size;

            /** Current occupied position */
            size_type m_current;

            /** Data of the byte array */
            byte_type * m_data;

        };

        /** 

            @nosubgrouping 
        */
        class fixed_byte_array {

            // --- fixed_byte_array - Friends ----------------------------------


        public:

            // --- fixed_byte_array - Public Constructors ----------------------

            /** Constructor

             @param [in] p_capacity is the maximum size of the byte array

             @throw if it was not possible to create it
            */
            fixed_byte_array( size_type p_capacity );

            /** Copy constructor

                @throw if it was not possible to create it
            */
            fixed_byte_array( const fixed_byte_array & p_array );

            /** Destructor */
            ~fixed_byte_array( );

            // --- fixed_byte_array - Public Accessors -------------------------

            /** Informs the amount of data in the byte array */
            size_type size( ) const;

            /** Informs the capacity of data in the byte array */
            size_type capacity( ) const;

            /** Tries to find the position of a byte

                @param [in] p_byte is the byte being searched
                @param [out] p_pos is the position of the @p p_byte, if found

                @return @p true id @p p_byte was found, @p false otherwiswe
             */
            bool find( byte_type p_byte, size_type & p_pos );

            /** Tries to retrieve a reference to a byte

                @param [in] p_byte is the byte being searched
                @param [out] p_found is a reference to the @p p_byte, if found

                @return @p true if @p p_byte was found, @p false otherwiswe
             */
            bool find( byte_type p_byte, byte_type * p_found );
            
            
            // --- fixed_byte_array - Public Helpers ---------------------------

            // --- fixed_byte_array - Public Processors ------------------------

            /** Assigns data at a position in the byte array

                @param [in] p_data is the data to be appended to the byte array
                @param [in] p_size is the size of @p p_data
                @param [in] p_pos is the position where @p p_data will be written
                at 

                @throw if it was not possible to assign @p p_data
             */
            void assign( const byte_type * p_data,
                         size_type p_size,
                         size_type p_pos );

            // --- fixed_byte_array - Public Operators -------------------------

            /** Equal-to */
            bool operator ==( const fixed_byte_array & p_array ) const;

            /** Not-equal-to */
            bool operator !=( const fixed_byte_array & p_array ) const;

            /** Assignment operator */
            fixed_byte_array & operator =( const fixed_byte_array & p_array );

            /** Retrieves the byte at a certain position

                @param [in] p_pos is a position in the byte array

                @throw if it was not possible to access @p p_index 
             */
            byte_type & operator[]( size_type p_pos );

            /** To iterate the byte array, from the begining to the end

                @tparam t_functor must implement:
                <tt>void operator( )( byte_type p_byte, size_type p_pos )</tt>,
                where @p p_byte is the value in @p p_pos
             */
            template <typename t_functor>
            void forward( t_functor & p_functor );

            /** To iterate the byte array, from the end to the begining

                @tparam t_functor must implement:
                <tt>void operator( )( byte_type p_byte, size_type p_pos )</tt>,
                where @p p_byte is the value in @p p_pos
             */
            template <typename t_functor>
            void backward( t_functor & p_functor );
            
            // --- fixed_byte_array - Public Attributes ------------------------

        private:

            

        };

        /** 

            @nosubgrouping 
        */
        class variable_byte_array {

            // --- variable_byte_array - Friends -------------------------------

            friend ostream & operator << ( ostream & p_out,
                                          const variable_byte_array & p_array );
            

        public:

            // --- variable_byte_array - Public Constructors -------------------

            /** Constructor

             @param [in] p_size is the initial size of the byte array

             @throw if it was not possible to create it
            */
            variable_byte_array( size_type p_size );

            /** Copy constructor

                @throw if it was not possible to create it
            */
            variable_byte_array( const variable_byte_array & p_array );

            /** Destructor */
            ~variable_byte_array( );

            // --- variable_byte_array - Public Accessors ----------------------

            /** Informs the amount of data in the byte array */
            size_type size( ) const;

            /** Tries to find the position of a byte

                @param [in] p_byte is the byte being searched
                @param [out] p_pos is the position of the @p p_byte, if found

                @return @p true id @p p_byte was found, @p false otherwiswe
             */
            bool find( byte_type p_byte, size_type & p_pos );

            /** Tries to retrieve a reference to a byte

                @param [in] p_byte is the byte being searched
                @param [out] p_found is a reference to the @p p_byte, if found

                @return @p true if @p p_byte was found, @p false otherwiswe
             */
            bool find( byte_type p_byte, byte_type * p_found );
            
            // --- variable_byte_array - Public Processors ---------------------

            /** Appends data to the byte array

                @param [in] p_data is the data to be appended to the byte array
                @param [in] p_size is the size of @p p_data

                @throw if it was not possible to append @p p_data
             */
            void append( const byte_type * p_data, size_type p_size );

            /** Assigns data at a position in the byte array

                @param [in] p_data is the data to be appended to the byte array
                @param [in] p_size is the size of @p p_data
                @param [in] p_pos is the position where @p p_data will be written
                at 

                @throw if it was not possible to assign @p p_data
             */
            void assign( const byte_type * p_data,
                         size_type p_size,
                         size_type p_pos );

            // --- variable_byte_array - Public Operators ----------------------

            /** Equal-to */
            bool operator ==( const variable_byte_array & p_array ) const;

            /** Not-equal-to */
            bool operator !=( const variable_byte_array & p_array ) const;

            /** Assignment operator */
            variable_byte_array & operator =( const variable_byte_array & p_array );

            /** Retrieves the byte at a certain position

                @param [in] p_pos is a position in the byte array

                @throw if it was not possible to access @p p_index 
             */
            byte_type & operator[]( size_type p_pos );

            /** To iterate the byte array, from the begining to the end

                @tparam t_functor must implement:
                <tt>void operator( )( byte_type p_byte, size_type p_pos )</tt>,
                where @p p_byte is the value in @p p_pos
             */
            template <typename t_functor>
            void forward( t_functor & p_functor );

            /** To iterate the byte array, from the end to the begining

                @tparam t_functor must implement:
                <tt>void operator( )( byte_type p_byte, size_type p_pos )</tt>,
                where @p p_byte is the value in @p p_pos
             */
            template <typename t_functor>
            void backward( t_functor & p_functor );
            
        private:

            // --- variable_byte_array - Private Processors --------------------

            /** Resizes the byte array, if necessary

             @throw if it was not possible to resize
            */
            void resize( size_type p_new_size );

            // --- variable_byte_array - Private Attributes --------------------

            /** Size of the byte array */
            size_type m_size;

            /** Current occupied position */
            size_type m_current;

            /** Data of the byte array */
            byte_type * m_data;
        };
    }
}

// =============================================================================
//                     I M P L E M E N T A T I O N
// =============================================================================

using namespace tenacitas::buffer;

#endif
